#include <stdio.h>
#include <stdlib.h>
#include "conio.h"
#include <limits.h>
#include <time.h>
#include <float.h>
#include "bitops.h"



#define FUNCS  7
#define NUM_PROCS 2

#define LOCK_P1 0
#define LOCK_P2 1
#define LOCK_PRINT 2
#define LOCK_FINAL 3

static int CDECL bit_shifter_mod(long int x);
static int CDECL bit_count_mod(long int x);

void lock(int id);
void unlock(int id);

/* Matriz global que guarda as somas dos bits para calcular o total no final */
volatile int resultados[NUM_PROCS][FUNCS];

/* Vetor que usado para sincronizar os processadores */
volatile int fim[NUM_PROCS] = {0};

void f(int p, int argc, char *argv[]){
  clock_t start, stop;
  double ct, cmin = DBL_MAX, cmax = 0;
  int i, s=0, cminix=0, cmaxix=0;
  long j, seed;
  int iterations;
  
  static int (* CDECL pBitCntFunc[FUNCS])(long) = {
    bit_count_mod,
    bitcount,
    ntbl_bitcnt,
    ntbl_bitcount,
    /*            btbl_bitcnt, DOESNT WORK*/
    BW_btbl_bitcount,
    AR_btbl_bitcount,
    bit_shifter_mod
  };
  static char *text[FUNCS] = {
    "Optimized 1 bit/loop counter",
    "Ratko's mystery algorithm",
    "Recursive bit count by nybbles",
    "Non-recursive bit count by nybbles",
    /*            "Recursive bit count by bytes",*/
    "Non-recursive bit count by bytes (BW)",
    "Non-recursive bit count by bytes (AR)",
    "Shift and count bits"
  };
  if (argc<2) {
    fprintf(stderr,"Usage: bitcnts <iterations>\n");
    exit(-1);
	}
  
  iterations=atoi(argv[1])/NUM_PROCS;

  if(p==NUM_PROCS-1)
    puts("Bit counter algorithm benchmark\n");

  for (i = 0; i < FUNCS; i++) {
    start = clock();
    
    for (j = resultados[p][i] = 0, seed = 13*p /*rand()*/; j < iterations; j++, seed += 13*NUM_PROCS){
      resultados[p][i] += pBitCntFunc[i](seed);
    }

    stop = clock();
    ct = (stop - start) / (double)CLOCKS_PER_SEC;
    
    if (ct < cmin) {
      cmin = ct;
      cminix = i;
    }
    else if (ct > cmax) {
      cmax = ct;
      cmaxix = i;
    }
    
    lock(LOCK_PRINT);
    printf("%-38s> Time: %7.3f sec.; Bits: %ld\n", text[i], ct, resultados[p][i]);
    unlock(LOCK_PRINT);
  }

  fim[p] = 1;

  /* Finaliza somando os totais */
  lock(LOCK_FINAL);
  for(i=0; i<NUM_PROCS; i++)
    s += fim[i];
  if(s == NUM_PROCS){
    printf("\n\nTotal\n\n");
    for(i=0; i<FUNCS; i++){
      s = 0;
      for(j=0; j<NUM_PROCS; j++)
        s += resultados[j][i];
      printf("%-38s> Bits: %ld\n", text[i], s);
    }
    printf("\n\n");
    fim[p] = 0;
  }
  unlock(LOCK_FINAL);
}

int main(int ac, char *av[]){
  volatile int c;
  int *ptr = (int *)(5*1024*1024);
  int i;

  c = *ptr;

  if(c < 40)
  {
    if(c)
    {
      asm("lui $8, 0x2");      
      for(i=0; i < c; i++)
        asm("sub $29, $29, $8");
    }

    f(c, ac, av);
  }
  else
    printf("Numero de processadores nao suportado");

  exit(0); // To avoid cross-compiler exit routine
  return 0; // Never executed, just for compatibility
}

/* Funcoes modificadas para acessar os perifericos */
static int CDECL bit_shifter_mod(long int x){
  int *p1 = (int *)(5*1024*1024+4), *p2 = (int *)(5*1024*1024+8), resultado;
  lock(LOCK_P1);
  *p1 = x;
  resultado = *p2;
  unlock(LOCK_P1);
  return resultado;
}

static int CDECL bit_count_mod(long int x){
  int *p1 = (int *)(5*1024*1024+12), *p2 = (int *)(5*1024*1024+16), resultado;
  lock(LOCK_P2);
  *p1 = x;
  resultado = *p2;
  unlock(LOCK_P2);
  return resultado;
}
/*******************************************************/


/* Funcoes para controlar locks. Deve ser informado o id do lock. */
void lock(int id){
  volatile int *p = (volatile int *)(5*1024*1024 + 20 + id*4);
  while(*p == 1);
}

void unlock(int id){
  volatile int *p = (volatile int *)(5*1024*1024 + 20 + id*4);
  *p = 0;
}

/**********************************************************************************/
